package com.eriloan.easyPage.service.impl;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.eriloan.easyPage.annotation.impl.FormParam;
import com.eriloan.easyPage.annotation.impl.MethodType;
import com.eriloan.easyPage.annotation.impl.QueryParam;
import com.eriloan.easyPage.annotation.impl.Route;
import com.eriloan.easyPage.annotation.impl.RouteParam;
import com.eriloan.easyPage.beans.ListenerBean;
import com.eriloan.easyPage.beans.RouteInfo;
import com.eriloan.easyPage.exception.ControllerException;
import com.eriloan.easyPage.exception.ConversionTypeException;
import com.eriloan.easyPage.exception.ListenerException;
import com.eriloan.easyPage.exception.OperateSpringBeanException;
import com.eriloan.easyPage.exception.UnknownException;
import com.eriloan.easyPage.listener.EasyPageListener;
import com.eriloan.easyPage.service.IEasyPageService;
import com.eriloan.easyPage.util.EasyPageContext;
import com.eriloan.easyPage.util.EasyPageTools;
import com.eriloan.easyPage.util.ScanClassPath;

/**
 * 
 * <p>Copyright: All Rights Reserved</p>
 * <p>Company: 北京荣之联科技股份有限公司 http://www.ronglian.com</p>
 * <p>Description: 框架核心处理 Service </p>
 * <p>Author:Eric Shi/史丙利</p>
 */
public class EasyPageServiceImpl implements IEasyPageService{

	/**
	 * 
	 * <p>Description:获取路由信息集合</p>
	 * <p>Author:Eric Shi/史丙利</p>
	 * 
	 * @Title: getRouteInfos
	 * @param packageDir
	 * @param filters
	 * @return
	 * @see com.Eriloan.EasyPage.service.IEasyPageService#getRouteInfos(java.lang.String,
	 *      java.lang.String)
	 */
	@Override
	public LinkedList<RouteInfo> getRouteInfos(String packageDir,String filters){
		LinkedList<RouteInfo> routeInfos = new LinkedList<RouteInfo>();

		List<String> classFilters = new ArrayList<String>();
		classFilters.add(filters);

		// 创建一个扫描处理器，排除内部类 扫描符合条件的类
		ScanClassPath handler = new ScanClassPath(true,true,classFilters);

		// 无参构造器，默认是排除内部类、并搜索符合规则
		// ScanClassPath handler = new ScanClassPath();

		// 递归扫描包：org.apache.commons.io 下符合自定义过滤规则的类
		Set<Class<?>> clazzList = handler.getPkgClassAll(packageDir,true);

		for(Class<?> clazz:clazzList){

			Method[] methods = clazz.getMethods();

			List<Method> methodList = new ArrayList<Method>();

			for(int i = 0;i < methods.length;i ++ ){
				Method m = methods[i];
				if( ! "Result_JsonTxt".equalsIgnoreCase(m.getName()) || ! "Result_JsonFile".equalsIgnoreCase(m.getName()) || ! "ForwardPage".equalsIgnoreCase(m.getName())){
					methodList.add(m);
				}
			}

			for(Method method:methodList){

				if(method.isAnnotationPresent(Route.class)){
					RouteInfo routeInfo = new RouteInfo();

					Route pathMethod = method.getAnnotation(Route.class);

					if(null != pathMethod){
						routeInfo.setClazz(clazz.getName());
						routeInfo.setMethod(method.getName());
						boolean boo = clazz.isAnnotationPresent(Route.class);
						Route pathClazz = null;
						if(boo){
							pathClazz = clazz.getAnnotation(Route.class);
						}
						String path = ((null != pathClazz)?EasyPageTools.deleteRightBar(pathClazz.value()):"") + EasyPageTools.deleteRightBar(pathMethod.value());
						routeInfo.setRoute(path.trim());
						routeInfo.setRouteLength(path.split("/").length);
					}

					MethodType methodType = method.getAnnotation(MethodType.class);

					if(null != methodType){
						routeInfo.setCallMethod(methodType.type().toString());
					}

					routeInfos.add(routeInfo);
				}
			}
		}
		return routeInfos;
	}

	/**
	 * 
	 * <br/>Description:返回所有的监听实现类
	 * <p>Author:Eric Shi/史丙利</p>
	 * 
	 * @param packageDir
	 * @return
	 */
	public LinkedList<ListenerBean> getlistenerBeans(String packageDir){
		LinkedList<ListenerBean> listenerBeans = new LinkedList<ListenerBean>();

		// 创建一个扫描处理器，排除内部类 扫描符合条件的类
		ScanClassPath handler = new ScanClassPath();

		// 递归扫描包：org.apache.commons.io 下符合自定义过滤规则的类
		Set<Class<?>> clazzList = handler.getPkgClassAll(packageDir,true);

		for(Class<?> clazz:clazzList){
			if(isImplementsEasyPageListener(clazz)){
				ListenerBean listenerBean = new ListenerBean();
				listenerBean.setName(clazz.getName());
				listenerBeans.add(listenerBean);
			}
		}

		return listenerBeans;
	}

	/**
	 * 
	 * <br/>Description:判断传入类是否实现的EasyPageListener接口
	 * <p>Author:Eric Shi/史丙利</p>
	 * 
	 * @param clazz
	 * @return
	 */
	public boolean isImplementsEasyPageListener(Class<?> clazz){

		if(clazz == null){ return false; }
		if(clazz.isInterface()){ return false; }
		if(Modifier.isAbstract(clazz.getModifiers())){ return false; }

		return EasyPageListener.class.isAssignableFrom(clazz);
	}

	/**
	 * 
	 * <p>Description:判断请求地址是否包含在路由表内，有则返回对应的实体</p>
	 * <p>Author:Eric Shi/史丙利</p>
	 * 
	 * @Title: isRouteInfos
	 * @param routeInfos
	 * @param route
	 * @param methodType
	 * @return
	 * @see com.Eriloan.EasyPage.service.IEasyPageService#isRouteInfos(java.util.LinkedList,
	 *      java.lang.String, java.lang.String)
	 */
	@Override
	public Map<String,Object> isRouteInfos(LinkedList<RouteInfo> routeInfos,String route,String methodType){

		Map<String,Object> map = new HashMap<String,Object>();

		LinkedList<RouteInfo> ris = new LinkedList<RouteInfo>();

		LinkedList<RouteInfo> ris_2 = new LinkedList<RouteInfo>();

		String inUrl = EasyPageTools.deleteRightBar(route);

		String[] inUrls = inUrl.split("/");

		int inUrlsLen = inUrls.length;

		for(RouteInfo routeInfo:routeInfos){
			if(routeInfo.getRouteLength() == inUrlsLen){
				ris_2.add(routeInfo);
			}
		}

		for(int i = 0;i < ris_2.size();i ++ ){

			List<Map<String,String>> routes = new ArrayList<Map<String,String>>();
			List<Map<String,String>> params = new ArrayList<Map<String,String>>();

			RouteInfo routeInfo = ris_2.get(i);

			String[] mUrls = EasyPageTools.deleteRightBar(routeInfo.getRoute()).split("/");

			for(int j = 1;j < mUrls.length;j ++ ){

				if( ! mUrls[j].equalsIgnoreCase(inUrls[j])){

					String sUrlLeft = mUrls[j].substring(0,1);
					String sUrlRight = mUrls[j].substring(mUrls[j].length() - 1,mUrls[j].length());

					if(sUrlLeft.equalsIgnoreCase("{") && sUrlRight.equalsIgnoreCase("}")){
						Map<String,String> paramMap = new HashMap<String,String>();
						paramMap.put(mUrls[j].toString(),EasyPageTools.deleteBigBrackets(inUrls[j].toString()));
						params.add(paramMap);
					}

				}

				if(mUrls[j].equalsIgnoreCase(inUrls[j])){
					Map<String,String> routeMap = new HashMap<String,String>();
					routeMap.put(inUrls[j],mUrls[j]);
					routes.add(routeMap);
				}

				routeInfo.setParams(params);
			}

			int sunParam = routes.size() + params.size();

			if(inUrlsLen - 1 == sunParam){
				ris.add(routeInfo);
			}
		}

		if(ris.size() == 1){
			if(ris.get(0).getCallMethod().equalsIgnoreCase(methodType)){
				map.put("isRouteInfos",true);
				map.put("routeInfo",ris.get(0));
			}else{
				map.put("isRouteInfos",false);
				map.put("routeInfo",null);
			}
		}else{
			map.put("isRouteInfos",false);
			map.put("routeInfo",null);
		}

		return map;
	}

	/**
	 * 
	 * <p>Description:根据参数执行对应的方法</p>
	 * <p>Author:Eric Shi/史丙利</p>
	 * 
	 * @Title: methodInvoke
	 * @param routeInfo
	 * @param listenerBeans
	 * @param isSupportSpring
	 * @return
	 * @throws ConversionTypeException
	 * @throws ListenerException
	 * @throws UnknownException
	 * 
	 * @see com.Eriloan.EasyPage.service.IEasyPageService#methodInvoke(javax.servlet.http.HttpServletResponse,
	 *      javax.servlet.http.HttpServletRequest,
	 *      com.Eriloan.EasyPage.beans.RouteInfo)
	 */
	public Object methodInvoke(RouteInfo routeInfo,LinkedList<ListenerBean> listenerBeans,boolean isSupportSpring) throws ConversionTypeException,ListenerException,OperateSpringBeanException,ControllerException,UnknownException{

		EasyPageContext easyPageContext = new EasyPageContext();
		HttpServletResponse response = easyPageContext.getResponse();
		HttpServletRequest request = easyPageContext.getRequest();

		String method = request.getMethod();

		String charset = easyPageContext.getCharset();

		Map<String,String> multipartParams = null;

		Map<String,String> putParams = null;

		Object obj = null;

		try{
			boolean isMultipartRequest = EasyPageTools.isMultipartRequest(request);

			if(isMultipartRequest){
				multipartParams = EasyPageTools.getMultipartParams(request,charset);
			}

			if("put".equalsIgnoreCase(method) || "delete".equalsIgnoreCase(method)){
				if(isMultipartRequest){
					putParams = multipartParams;
				}else{
					putParams = getPutParams(request,charset);
				}
			}

			Class<?> clazz = EasyPageTools.getClassByClassLoader(EasyPageTools.getClassLoader(),routeInfo.getClazz());

			Method[] mds = clazz.getMethods();

			for(int i = 0;i < mds.length;i ++ ){
				Method md = mds[i];

				if(routeInfo.getMethod().equals(md.getName())){

					Type[] mdTypes = md.getGenericParameterTypes();

					Annotation[][] annoParams = md.getParameterAnnotations();

					Object[] objs = new Object[annoParams.length];

					for(int j = 0;j < annoParams.length;j ++ ){

						Annotation annotation = annoParams[j][0];

						if("FormParam".equals(annotation.annotationType().getSimpleName())){
							FormParam param = (FormParam) annotation;

							String pmType = mdTypes[j].toString();

							if(isMultipartRequest){
								objs[j] = EasyPageTools.createObjectByParamType(pmType,multipartParams.get(param.value()));
							}else{
								if("put".equalsIgnoreCase(method) || "delete".equalsIgnoreCase(method)){
									objs[j] = EasyPageTools.createObjectByParamType(pmType,putParams.get(param.value()));
								}else{
									objs[j] = EasyPageTools.createObjectByParamType(pmType,request.getParameter(param.value()));
								}
							}
						}else if("QueryParam".equals(annotation.annotationType().getSimpleName())){
							QueryParam param = (QueryParam) annotation;

							String pmType = mdTypes[j].toString();
							if(isMultipartRequest){
								objs[j] = EasyPageTools.createObjectByParamType(pmType,EasyPageTools.urlDecoder(multipartParams.get(param.value()),charset));
							}else{
								if("put".equalsIgnoreCase(method) || "delete".equalsIgnoreCase(method)){
									objs[j] = EasyPageTools.createObjectByParamType(pmType,EasyPageTools.urlDecoder(putParams.get(param.value()),charset));
								}else{
									objs[j] = EasyPageTools.createObjectByParamType(pmType,EasyPageTools.urlDecoder(request.getParameter(param.value()),charset));
								}
							}

						}else if("RouteParam".equals(annotation.annotationType().getSimpleName())){
							RouteParam param = (RouteParam) annotation;

							String val = EasyPageTools.getMapValue(routeInfo.getParams(),EasyPageTools.addBigBrackets(param.value()));

							String pmType = mdTypes[j].toString();

							objs[j] = EasyPageTools.createObjectByParamType(pmType,EasyPageTools.urlDecoder(val,charset));
						}
					}

					for(int j = 0;j < listenerBeans.size();j ++ ){
						ListenerBean listenerBean = listenerBeans.get(j);

						Class<?> listenerClazz = EasyPageTools.getClassByClassLoader(EasyPageTools.getClassLoader(),listenerBean.getName());

						Method[] listenerMethods = listenerClazz.getMethods();

						for(int k = 0;k < listenerMethods.length;k ++ ){
							Method m = listenerMethods[k];
							if("beforeListrner".equalsIgnoreCase(m.getName())){

								Object[] listenerBeforeMethodParam = new Object[4];

								listenerBeforeMethodParam[0] = clazz.getName();
								listenerBeforeMethodParam[1] = md.getName();
								listenerBeforeMethodParam[2] = request;
								listenerBeforeMethodParam[3] = response;

								try{
									m.invoke(listenerClazz.newInstance(),listenerBeforeMethodParam);
								}catch(Exception e){
									throw new ListenerException("BeforeListrner Error.",e);
								}
								break;
							}
						}
					}

					Object o = null;

					if(isSupportSpring){
						try{
							Class<?> springClass = EasyPageTools.getClassByClassForName("com.eriloan.easyPage.plugins.spring.SpringBeansManagement");

							Method sm = null;

							Method[] smds = springClass.getMethods();

							for(Method m:smds){
								if("getSrpingBean".equals(m.getName())){
									sm = m;
									break;
								}
							}

							Object[] sobjs = {clazz};

							o = sm.invoke(springClass.newInstance(),sobjs);

						}catch(Exception e){
							throw new OperateSpringBeanException("Operate spring bean error.Bean Name:" + clazz.getName(),e);
						}
					}else{
						try{
							o = clazz.newInstance();
						}catch(Exception e){
							throw new ControllerException("New controll bean error.Bean Name:" + clazz.getName(),e);
						}
					}

					try{
						obj = md.invoke(o,objs);
					}catch(Exception e){
						throw new ControllerException("Execute controll bean error.Bean Name:" + clazz.getName(),e);
					}

					for(int j = 0;j < listenerBeans.size();j ++ ){
						ListenerBean listenerBean = listenerBeans.get(j);

						Class<?> listenerClazz = EasyPageTools.getClassByClassLoader(EasyPageTools.getClassLoader(),listenerBean.getName());

						Method[] listenerMethods = listenerClazz.getMethods();

						for(int k = 0;k < listenerMethods.length;k ++ ){
							Method m = listenerMethods[k];
							if("afterListrner".equalsIgnoreCase(m.getName())){

								Object[] listenerBeforeMethodParam = new Object[4];

								listenerBeforeMethodParam[0] = clazz.getName();
								listenerBeforeMethodParam[1] = md.getName();
								listenerBeforeMethodParam[2] = request;
								listenerBeforeMethodParam[3] = response;
								try{
									m.invoke(listenerClazz.newInstance(),listenerBeforeMethodParam);
								}catch(Exception e){
									throw new ListenerException("AfterListrner Error.",e);
								}
								break;
							}
						}
					}
					break;
				}
			}
		}catch(Exception e){
			throw new UnknownException("Execute controll bean error.",e);
		}
		return obj;
	}

	public Map<String,String> getPutParams(HttpServletRequest request,String charset){
		InputStreamReader in = null;
		try{
			in = new InputStreamReader(request.getInputStream(),charset);
		}catch(UnsupportedEncodingException e){
			// e.printStackTrace();
		}catch(IOException e){
			// e.printStackTrace();
		}

		String urlParam = EasyPageTools.getServletInputStream(in);
		return EasyPageTools.getUrlParamByKey(urlParam);
	}
}
